// --------------------------------------------------------------------------------------------------------------------
// <copyright file="UIDictionaryService.cs" company="Open Trader">
//   Copyright (c) David Denis (david.denis@systemathics.com)
// </copyright>
// <summary>
//   |  Open Trader - The Open Source Systematic Trading Platform
//   |
//   |  This program is free software: you can redistribute it and/or modify
//   |  it under the terms of the GNU General Public License as published by
//   |  the Free Software Foundation, either version 2 of the License, or
//   |  (at your option) any later version.
//   |
//   |  This program is distributed in the hope that it will be useful,
//   |  but WITHOUT ANY WARRANTY; without even the implied warranty of
//   |  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//   |  GNU General Public License for more details.
//   |
//   |  You should have received a copy of the GNU General Public License
//   |  along with this program.  If not, see http://www.gnu.org/licenses
//   |
//   |  Up to date informations about Open Trader can be found at :
//   |    http://opentrader.org
//   |    http://opentrader.codeplex.com
//   |
//   |  For professional services, please visit us at :
//   |    http://www.systemathics.com
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Org.OpenTrader.Framework.Forge.UI.Forms
{
    #region Using Directives

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Drawing;
    using System.Linq;

    using Org.OpenTrader.Framework.Enums;
    using Org.OpenTrader.Framework.Forge.Attributes;
    using Org.OpenTrader.Framework.Forge.DataCache;
    using Org.OpenTrader.Framework.Forge.Enums;
    using Org.OpenTrader.Framework.Forge.Interfaces;
    using Org.OpenTrader.Framework.LinqKit;
    using Org.OpenTrader.Framework.LiveObjects;
    using Org.OpenTrader.Framework.LiveObjects.Interfaces;
    using Org.OpenTrader.Framework.UI.Forms;

    #endregion

    /// <summary>
    /// The ui dictionary service.
    /// </summary>
    public partial class UIDictionaryService : UIControl
    {
        #region Constants and Fields

        /// <summary>
        /// The dicoService.
        /// </summary>
        private readonly IDictionaryService dicoService;

        /// <summary>
        /// The feedService.
        /// </summary>
        private readonly IFeedService feedService;

        /// <summary>
        /// The orderService.
        /// </summary>
        private readonly IOrderService orderService;

        #endregion

        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="UIDictionaryService"/> class.
        /// </summary>
        public UIDictionaryService()
        {
            this.InitializeComponent();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="UIDictionaryService"/> class.
        /// </summary>
        /// <param name="dictionaryService">
        /// The dictionaryService.
        /// </param>
        /// <param name="feedService">
        /// The feedService.
        /// </param>
        /// <param name="orderService">
        /// The orderService.
        /// </param>
        public UIDictionaryService(IDictionaryService dictionaryService, IFeedService feedService, IOrderService orderService)
        {
            this.InitializeComponent();

            // This is the source dictionary
            this.dicoService = dictionaryService;
            this.feedService = feedService;
            this.orderService = orderService;

            // Available instrument class types
            var containerId = (dictionaryService.Descriptor as ILocalLiveObjectDescriptor).ContainerID;
            var container = Singleton<LiveObjectsRegistry>.Instance.GetContainer(containerId);
            var implementors = container.GetImplementors(typeof(IInstrumentStaticData).FullName);
            foreach (var s in implementors)
            {
                var attributes = container.GetAttributes(s);
                var implementor = new Implementor(s, attributes);

                // this.implementors[s] = implementor;
                this.createInstrumentType.Items.Add(implementor);
            }

            // Available markets
            this.createInstrumentMarket.Items.AddRange(Singleton<DC>.Instance.GetMarketsFromCache(dictionaryService.Descriptor.ID));

            // Available TSR
            var myTsrs = Singleton<DC>.Instance.LoadTSRs(dictionaryService.Descriptor.ID);
            this.createInstrumentTsr.Items.AddRange(myTsrs);
            this.editTsr.Items.AddRange(myTsrs);

            // Available scopes
            IList<string> scopes = new List<string>(System.Enum.GetNames(typeof(ETradingScope)));
            this.createInstrumentScope.Items.AddRange(scopes.ToArray());

            // Provider for feed wirings
            var tdpFeed = new WiringTypeDescriptionProvider<FeedWiring>(this.feedService.Wires);
            TypeDescriptor.AddProvider(tdpFeed, typeof(IFeedWiring));
            TypeDescriptor.AddProvider(tdpFeed, typeof(FeedWiring));

            // Provider for order wirings
            var tdpOrder = new WiringTypeDescriptionProvider<OrderWiring>(this.orderService.Wires);
            TypeDescriptor.AddProvider(tdpOrder, typeof(IOrderWiring));
            TypeDescriptor.AddProvider(tdpOrder, typeof(OrderWiring));

            // All the instruments
            var instrumentPredicate = PredicateBuilder.True<IInstrumentStaticData>();
            var lookupstatus = this.dicoService.LookupInstruments(instrumentPredicate);
            if (lookupstatus)
            {
                var instrumentstaticdatas = lookupstatus.NestedObject;
                this.instrumentList.Instruments = new List<IInstrumentStaticData>(instrumentstaticdatas);
            }

            // Put currencies available for "create instrument"
            foreach (var enumvalue in Enum.GetValues(typeof(ECurrency)))
            {
                this.createInstrumentCurrency.Items.Add(enumvalue);
            }

            // Click the header to change instrument
            // instrumentList.UI.FormsVirtualGrid.RowHeaderMouseClick += new System.Windows.Forms.DataGridViewCellMouseEventHandler(UIVirtualGridRowHeaderMouseClick);
            this.instrumentList.UIVirtualGrid.RowEnter += new System.Windows.Forms.DataGridViewCellEventHandler(this.UIVirtualGridRowEnter);
            this.instrumentDetailsgrid.PropertyValueChanged +=
                new System.Windows.Forms.PropertyValueChangedEventHandler(this.OnInstrumentDetailsgridPropertyValueChanged);
        }

        #endregion

        #region Methods

        /// <summary>
        /// The button 1_ click.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        private void ImportButtonClick(object sender, EventArgs e)
        {
            // using (var excel = new ExcelXslxReader(@"c:\test.xlsx"))
            // {
            // // New empty currency prices list
            // IDictionary<string,double> currencyPrices = new Dictionary<string, double>();

            // // Using the excel Cells of "CurrencyPrices" sheet
            // // Column A is currency ISO code
            // // Column B is rate to apply
            // bool exit=false;
            // var excelrow = 1;
            // do
            // {
            // var currencystatus = excel.XLGetCellValue<string>("CurrencyPrices", "A" + excelrow);
            // var currencypricestatus = excel.XLGetCellValue<string>("CurrencyPrices", "B" + excelrow);

            // if( currencystatus && currencypricestatus )
            // {
            // var currency = currencystatus.NestedObject;
            // var currencyprice = double.Parse(currencypricestatus.NestedObject,ExcelXslxReader.CultureInfo);

            // // Add to hashmap
            // currencyPrices[currency] = currencyprice;
            // }
            // else
            // {
            // exit = true;
            // }

            // excelrow++;
            // }
            // while (!exit);
            // }
        }

        /// <summary>
        /// The createInstrumentButton_ click.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        private void OnCreateInstrumentButtonClick(object sender, EventArgs e)
        {
            var mkt = this.createInstrumentMarket.SelectedItem as IMarket;
            var tsr = this.createInstrumentTsr.SelectedItem as ITickSizeRule;
            var instrument = Singleton<DC>.Instance.CreateInstrumentStaticDataAndCache(
                mkt.ID, 
                tsr.ID, 
                (this.createInstrumentType.SelectedItem as Implementor).FullName, 
                this.createInstrumentName.Text, 
                this.createInstrumentCode.Text, 
                this.createInstrumentIsin.Text, 
                (ECurrency)this.createInstrumentCurrency.SelectedItem, 
                EnumDecoder<ETradingScope>.Parse(this.createInstrumentScope.SelectedItem as string));

            if (instrument != null)
            {
                this.instrumentList.Instruments.Add(instrument);
                this.instrumentList.UIVirtualGrid.Refresh();
            }

            this.createInstrumentType.SelectedIndex = -1;
            this.createInstrumentMarket.SelectedIndex = -1;
            this.createInstrumentScope.SelectedIndex = -1;
            this.createInstrumentTsr.SelectedIndex = -1;
            this.createInstrumentCurrency.SelectedIndex = -1;
            this.createInstrumentCode.Text = string.Empty;
            this.createInstrumentName.Text = string.Empty;
            this.createInstrumentIsin.Text = string.Empty;

            this.createInstrumentMarket.Enabled = false;
            this.createInstrumentCurrency.Enabled = false;
            this.createInstrumentIsin.Enabled = false;
            this.createInstrumentName.Enabled = false;
            this.createInstrumentCode.Enabled = false;
            this.createInstrumentTsr.Enabled = false;
            this.createInstrumentScope.Enabled = false;

            this.createInstrumentType.Enabled = true;
        }

        /// <summary>
        /// Te createInstrumentCode_ text changed.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        private void OnCreateInstrumentCodeTextChanged(object sender, EventArgs e)
        {
            if (this.createInstrumentCode.Text.Length <= 0)
            {
                return;
            }

            this.createInstrumentName.Enabled = true;
        }

        /// <summary>
        /// The createInstrumentCurrency_ selected index changed.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        private void OnCreateInstrumentCurrencySelectedIndexChanged(object sender, EventArgs e)
        {
            if (this.createInstrumentCurrency.SelectedIndex < 0)
            {
                return;
            }

            this.createInstrumentButton.Enabled = true;
        }

        /// <summary>
        /// The createInstrumentIsin_ text changed.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        private void OnCreateInstrumentIsinTextChanged(object sender, EventArgs e)
        {
            if (this.createInstrumentIsin.Text.Length <= 0)
            {
                return;
            }

            this.createInstrumentCurrency.Enabled = true;
            return;
        }

        /// <summary>
        /// The createInstrumentMarket_ selected index changed.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        private void OnCreateInstrumentMarketSelectedIndexChanged(object sender, EventArgs e)
        {
            if (this.createInstrumentMarket.SelectedIndex < 0)
            {
                return;
            }

            this.createInstrumentTsr.Enabled = true;
        }

        /// <summary>
        /// The createInstrumentName_ text changed.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        private void OnCreateInstrumentNameTextChanged(object sender, EventArgs e)
        {
            this.createInstrumentIsin.Enabled = false;

            var instrumentName = this.createInstrumentName.Text;
            var instrumentTradingScope = EnumDecoder<ETradingScope>.Parse(this.createInstrumentScope.SelectedItem as string);
            if (instrumentName.Length > 0)
            {
                // Look if this name was already taken for this scope
                var p = PredicateBuilder.True<IInstrumentStaticData>();
                p = p.And(mm => mm.Name == instrumentName);
                p = p.And(mm => mm.TradingScope == instrumentTradingScope);
                var lookupstatus = this.dicoService.LookupInstruments(p);

                // Doesn't exist yet -> allow
                if (lookupstatus && lookupstatus.NestedObject.Length == 0)
                {
                    this.createInstrumentIsin.Enabled = true;
                }
            }
        }

        /// <summary>
        /// The createInstrumentScope_ selected index changed.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        private void OnCreateInstrumentScopeSelectedIndexChanged(object sender, EventArgs e)
        {
            if (this.createInstrumentScope.SelectedIndex < 0)
            {
                return;
            }

            this.createInstrumentCode.Enabled = true;
        }

        /// <summary>
        /// The createInstrumentTsr_ selected index changed.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        private void OnCreateInstrumentTsrSelectedIndexChanged(object sender, EventArgs e)
        {
            if (this.createInstrumentMarket.SelectedIndex < 0)
            {
                return;
            }

            this.createInstrumentScope.Enabled = true;
        }

        /// <summary>
        /// The createInstrumentType_ selected index changed.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        private void OnCreateInstrumentTypeSelectedIndexChanged(object sender, EventArgs e)
        {
            if (this.createInstrumentType.SelectedIndex < 0)
            {
                return;
            }

            this.createInstrumentMarket.Enabled = true;
        }

        /// <summary>
        /// The createMarketButton_ click.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        private void OnCreateMarketButtonClick(object sender, EventArgs e)
        {
            var mkt = Singleton<DC>.Instance.CreateMarketAndCache(this.dicoService.Descriptor.ID, this.createMarketPath.Text);
            if (mkt != null)
            {
                this.createInstrumentMarket.Items.Add(mkt);
            }

            // Clear the textbox
            this.createMarketPath.Text = string.Empty;
        }

        /// <summary>
        /// The createMarketPath_ text changed.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        private void OncreateMarketPathTextChanged(object sender, EventArgs e)
        {
            this.createMarketButton.Enabled = false;

            var mktPath = this.createMarketPath.Text;
            if (mktPath.Length > 0)
            {
                // Look if this path was already taken
                var p = PredicateBuilder.True<IMarket>();
                p = p.And(mm => mm.Path == mktPath);
                var lookupstatus = this.dicoService.LookupMarkets(p);

                // Doesn't exist yet -> Allow
                if (lookupstatus && lookupstatus.NestedObject.Length == 0)
                {
                    this.createMarketButton.Enabled = true;
                }
            }
        }

        /// <summary>
        /// The createTsrButton_ click.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        private void OnCreateTsrButtonClick(object sender, EventArgs e)
        {
            var tsr = Singleton<DC>.Instance.CreateTSR(this.dicoService.Descriptor.ID, this.createTsr.Text);

            if (tsr != null)
            {
                this.createInstrumentTsr.Items.Add(tsr);
                this.editTsr.Items.Add(tsr);
            }

            this.createTsr.Text = string.Empty;
        }

        /// <summary>
        /// The createTsr_ text changed.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        private void OnCreateTsrTextChanged(object sender, EventArgs e)
        {
            this.createTsrButton.Enabled = false;

            var tsrName = this.createTsr.Text;
            if (tsrName.Length > 0)
            {
                this.createTsrButton.Enabled = true;
            }
        }

        /// <summary>
        /// The editTsrButton_ click.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        private void OnEditTsrButtonClick(object sender, EventArgs e)
        {
            var tsr = this.editTsr.SelectedItem as TickSizeRule;
            if (tsr == null)
            {
                return;
            }

            // Edit
            var ctrl = new UITickSizeRule(tsr);
            var form = ctrl.CreateContainingForm("TickSizeRule");
            form.Visibility = EUIFormVisibility.ShowDialog;

            // Save
            Singleton<DC>.Instance.SaveTSR(tsr);

            // Unselect
            this.editTsr.SelectedIndex = -1;
        }

        /// <summary>
        /// The instrumentDetailsgrid_ property value changed.
        /// </summary>
        /// <param name="s">
        /// The s.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        private void OnInstrumentDetailsgridPropertyValueChanged(object s, System.Windows.Forms.PropertyValueChangedEventArgs e)
        {
            var i = this.instrumentDetailsgrid.SelectedObject as IInstrumentStaticData;
            Singleton<DC>.Instance.SaveInstrumentStaticDataAndCache(i);
        }

        /// <summary>
        /// The ui virtual grid_ row enter.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        private void UIVirtualGridRowEnter(object sender, System.Windows.Forms.DataGridViewCellEventArgs e)
        {
            if (e.RowIndex < this.instrumentList.Instruments.Count)
            {
                var i = this.instrumentList.Instruments[e.RowIndex];
                this.instrumentDetailsgrid.SelectedObject = i;
                this.instrumentDetailsgrid.Refresh();
            }
        }

        #endregion

        /// <summary>
        /// The implementor.
        /// </summary>
        internal class Implementor
        {
            #region Constructors and Destructors

            /// <summary>
            /// Initializes a new instance of the <see cref="Implementor"/> class.
            /// </summary>
            /// <param name="fullName">
            /// The full name.
            /// </param>
            /// <param name="attributes">
            /// The attributes.
            /// </param>
            public Implementor(string fullName, Attribute[] attributes)
            {
                this.FullName = fullName;
                var friendlyDisplayAttributes = from a in attributes where a is InstrumentFriendlyDisplayAttribute select a;

                if (friendlyDisplayAttributes.Count() == 1)
                {
                    var attribute = friendlyDisplayAttributes.First() as InstrumentFriendlyDisplayAttribute;
                    this.FriendlyName = string.Format("{0}   ({1})", attribute.Text, attribute.Description);
                }
                else
                {
                    var name = this.FullName.Substring(fullName.LastIndexOf('.') + 1).Replace("StaticData", string.Empty).Replace("Instrument", string.Empty);
                    this.FriendlyName = string.Format("{0}   ({1})", name, fullName);
                }
            }

            #endregion

            #region Properties

            /// <summary>
            /// Gets FriendlyName.
            /// </summary>
            public string FriendlyName { get; private set; }

            /// <summary>
            /// Gets FullName.
            /// </summary>
            public string FullName { get; private set; }

            #endregion

            #region Public Methods

            /// <summary>
            /// The to string.
            /// </summary>
            /// <returns>
            /// The to string.
            /// </returns>
            public override string ToString()
            {
                return this.FriendlyName;
            }

            #endregion
        }
    }
}

/*                // All instruments
          {
              var instrumentPredicate = PredicateBuilder.True<IInstrumentStaticData>();
              IInstrumentStaticData[] foundResults = dicoService.CreateInstruments(instrumentPredicate);
          }

          // All instruments below /LSE
          {

              var instrumentPredicate = PredicateBuilder.True<IInstrumentStaticData>();
              instrumentPredicate = instrumentPredicate.And(i => i.Market.Path.StartsWith("/LSE"));
              IInstrumentStaticData[] foundResults = dicoService.CreateInstruments(instrumentPredicate);
          }

          // All IInstrumentStaticDataEquity
          {

              var instrumentPredicate = PredicateBuilder.True<IInstrumentStaticData>();
              instrumentPredicate = instrumentPredicate.And(i => i is IInstrumentStaticDataEquity);
              IInstrumentStaticData[] foundResults = dicoService.CreateInstruments(instrumentPredicate);
          }

          // All IInstrumentStaticDataEquity,  inside LSE, and in banks sector 
          {
              var instrumentPredicate = PredicateBuilder.True<IInstrumentStaticData>();
              instrumentPredicate = instrumentPredicate.And(i => i is IInstrumentStaticDataEquity);
              instrumentPredicate = instrumentPredicate.And(i => i.Market.Path.StartsWith("LSE"));
              instrumentPredicate = instrumentPredicate.And(i => (i as IInstrumentStaticDataEquity).Sector.Equals("Banks"));
              IInstrumentStaticData[] foundResults = dicoService.CreateInstruments(instrumentPredicate);
          }


          // All IInstrumentStaticDataEquityVanillaOption, inside LSE, written on an underlying in euro
          {
              var instrumentPredicate = PredicateBuilder.True<IInstrumentStaticData>();
              instrumentPredicate = instrumentPredicate.And(i => i is IInstrumentStaticDataEquityVanillaOption);
              instrumentPredicate = instrumentPredicate.And(i => i.Market.Path.StartsWith("LSE"));
              instrumentPredicate = instrumentPredicate.And(i => (i as IInstrumentStaticDataEquityVanillaOption).Underlying.Currency.Equals("EUR"));
              IInstrumentStaticData[] foundResults = dicoService.CreateInstruments(instrumentPredicate);
          }

          // All IInstrumentStaticDataEquity below /LSE
          {
              var instrumentPredicate = PredicateBuilder.True<IInstrumentStaticData>();
              instrumentPredicate = instrumentPredicate.And(i => i is IInstrumentStaticDataEquity);
              instrumentPredicate = instrumentPredicate.And(i => i.Market.Path.StartsWith("/LSE"));
              IInstrumentStaticData[] foundResults = dicoService.CreateInstruments(instrumentPredicate);
          }

          // All IInstrumentStaticDataEquity tradable at this time
          {
              var instrumentPredicate = PredicateBuilder.True<IInstrumentStaticData>();
              instrumentPredicate = instrumentPredicate.And(i => i is IInstrumentStaticDataEquity);
              instrumentPredicate = instrumentPredicate.And(i => i.Market.Opening > Timings.Now && Timings.Now < i.Market.Closing);
              IInstrumentStaticData[] foundResults = dicoService.CreateInstruments(instrumentPredicate);
          }*/